import breve
import random

class Controller(breve.Control):
	def __init__(self):
		breve.Control.__init__(self)
		self.setBackgroundColor(breve.vector())
		self.enableLighting()
		self.env = Room(breve.vector(20, 0, 20), 10, [ExitBot, GridBot], [50, 50])
		
	def iterate(self):
		breve.Control.iterate(self)
		self.updateNeighbors()	

	def catchKeyXDown(self):
		exitBots = self.env.fetch.some(ExitBot, 20)
		while len(exitBots) > 0:
			exitBots.pop().leaveRoom()
	
class GridBot(breve.Mobile):
	def __init__(self):
		breve.Mobile.__init__(self)
		self.handleCollisions('GridBot', 'botCollision')
	
	def iterate(self):
		breve.Mobile.iterate(self)
		self.neighborList = self.getNeighbors()	
		
	def botCollision(self):
		return 1
		
	def look(self, xDirection, zDirection, Type):
		x = 0
		z = 0
	
		if xDirection != "Center":
			if xDirection == "Left": x -= (self.unit - 1)
			else: x += (self.unit - 1)

		if zDirection != "Center":
			if zDirection == "Up": z -= (self.unit - 1)
			else: z += (self.unit - 1)
		
		while len(self.neighborList) > 0:
			neighbor = self.neighborList.pop()
			if (neighbor.getLocation()[0] > self.getLocation()[0] - self.unit / 2 + x and
			    neighbor.getLocation()[0] < self.getLocation()[0] - self.unit / 2 + self.unit + x and
			    neighbor.getLocation()[2] < self.getLocation()[2] + self.unit / 2 + z and
				neighbor.getLocation()[2] > self.getLocation()[2] + self.unit / 2 - self.unit + z):
					self.neighborList = self.getNeighbors()
					return isinstance(neighbor, Type)
		
		self.neighborList = self.getNeighbors()
		return False			
			
	def walk(self, xDirection, zDirection):
		x = 0
		z = 0
		
		if xDirection != "Center":
			if xDirection == "Left": x -= self.unit
			else: x += self.unit	
	
		if zDirection != "Center":
			if zDirection == "Up": z -= self.unit
			else: z += self.unit
	
		self.move(breve.vector(
			self.getLocation()[0] + x, 
			self.getLocation()[1], 
			self.getLocation()[2] + z))
	
class ExitBot(GridBot):
	def __init__(self):
		GridBot.__init__(self)
		
	def iterate(self):
		GridBot.iterate(self)
		if self.delay > 0: self.delay -= 1
		else:
			exitDirection = self.getExitDirection()
			
			if not self.getOut or self.getOut and self.look(exitDirection[0], exitDirection[1], GridBot):
				possibleDirections = self.getPossibleDirections()
				if len(possibleDirections) > 0:
					randomDirection = random.randint(0, len(possibleDirections) - 1)
					self.walk(possibleDirections[randomDirection][0], 
							  possibleDirections[randomDirection][1])
			else: self.walk(exitDirection[0], exitDirection[1])
							  
			self.delay = self.maxDelay	
			
	def getExitDirection(self):
		x = "Center"
		z = "Center"
		if self.getLocation()[0] < self.exit[0]: x = "Right"
		if self.getLocation()[0] > self.exit[0]: x = "Left"
		if self.getLocation()[2] < self.exit[2]: z = "Down"
		if self.getLocation()[2] > self.exit[2]: z = "Up"
		return [x, z]	
		
	def getPossibleDirections(self):	
		possibleDirections = [["Left", "Up"],["Center", "Up"],["Right", "Up"],
		              ["Left", "Center"],["Center", "Center"],["Right", "Center"],
					  ["Left", "Down"],["Center", "Down"],["Right", "Down"]]
		
		directions = []
		while len(possibleDirections) > 0:
			direction = possibleDirections.pop()
			if (not self.look(direction[0], direction[1], GridBot) and
			    not self.look(direction[0], direction[1], Wall)):
					directions.append(direction)
		
		return directions	
	
	def leaveRoom(self):
		self.getOut = True
		self.setColor(breve.randomExpression(breve.vector(1, 0, 0)))
		
					
class Room(breve.Real):
	def __init__(self, dim, unit, botTypes, botCounts):
		breve.Real.__init__(self)
		self.unit = unit
		self.width = dim[0]
		self.height = dim[2]
		self.exit = breve.vector(5, 5, -5)
		self.delay = 10
		self.getController().pointCamera(
			breve.vector(self.width * unit / 2, 0, -self.height * unit / 2), 
			breve.vector(0, 200, self.height * unit))
		
		for x in range(-1, self.width + 1):
			Wall(breve.vector(x * unit + unit / 2, unit / 2, 0), 
				 breve.vector(unit, unit, .1))
			Wall(breve.vector(x * unit + unit / 2, unit / 2, -self.height * unit), 
				 breve.vector(unit, unit, .1))
		
		for z in range(-1, self.height + 1):
			Wall(breve.vector(0, unit / 2, -z * unit - unit / 2), 
				 breve.vector(.1, unit, unit))
			Wall(breve.vector(self.width * unit, unit / 2, -z * unit - unit / 2), 
				 breve.vector(.1, unit, unit))

		#for z in range(0, self.height):
			#for x in range(0, self.width):
				#Ground(breve.vector(x * unit + unit / 2, 0, -z * unit - unit / 2), 
					   #breve.vector(unit, .1, unit))
				
		Ground(breve.vector((self.width / 2) * unit, 0, -(self.height / 2) * unit), 
					   breve.vector(self.width * unit, .1, self.height * unit))	
		
		botCount = 0
		for c in range(0, len(botCounts)):
			botCount += botCounts[c]
		
		if botCount > self.width * self.height:
			print "Too many bots!"
		self.placeBots(botTypes, botCounts)
		
		BlackHole(unit).move(self.exit)
		self.fetch = Fetcher(
			breve.vector(self.width * unit / 2, -unit / 2, 
						-self.height * unit / 2), 
			max(self.width, self.height) * unit)

	def placeBots(self, botTypes, botCounts):
		positions = []
		positionCount = self.width * self.height
		
		for i in range(0, positionCount):
			positions.append(True)

		while len(botCounts) > 0:
			while True:
				spot = random.randint(0, positionCount - 1)
				if positions[spot]: break
				
			positions[spot] = False	
			bot = botTypes[0]()
			bot.move(breve.vector(
				int(spot % self.height) * self.unit + self.unit / 2, self.unit / 2, 
			   -int(spot / self.height) * self.unit - self.unit / 2))
			bot.unit = self.unit
			bot.setShape(breve.createInstances(breve.Cube, 1).initWith(
				breve.vector(self.unit, self.unit, self.unit)))
			bot.setNeighborhoodSize(self.unit * 2)
			
			if isinstance(bot, ExitBot):
				bot.exit = self.exit
				bot.delay = self.delay
				bot.maxDelay = self.delay
				bot.setShape(breve.createInstances(breve.Sphere, 1).initWith(self.unit / 2))
				bot.setColor(breve.randomExpression(breve.vector(0, 0, 1)))
				bot.getOut = False
			
			botCounts[0] -= 1
			if botCounts[0] == 0:
				botCounts.pop(0)
				botTypes.pop(0)
			
	def getOut(self):
		self.exit = True
		self.setColor(breve.vecter(1, 0, 0))
			
	def setExit(self, exit):
		self.exit = breve.vector(exit[0] * unit + unit / 2, unit / 2, -exit[1] * unit - unit / 2)
	
	def setDelay(self, delay):
		self.dealy = delay

	def getWidth(self): return self.width
	def getHeight(self): return self.height
	def getUnit(self): return self.unit
	
class Obstruction(breve.Stationary):
	def __init__(self, position, dimension, color):
		breve.Stationary.__init__(self)
		self.setTexture(-1)
		self.setShape(breve.createInstances(breve.Cube, 1).initWith(dimension))
		self.setColor(color)
		self.move(position)
	
class Wall(Obstruction):
	def __init__(self, position, dimension):
		Obstruction.__init__(self, position, dimension, breve.vector(1.0, 1.0, 1.0))
		#self.setBitmapTransparency(0.5)	
		
class Ground(Obstruction):
	def __init__(self, position, dimension):
		Obstruction.__init__(self, position, dimension, breve.vector(0.0, 0.3, 0.0))
		#self.setBitmapTransparency(0.5)
		
class BlackHole(breve.Mobile):
	def __init__(self, unit):
		breve.Mobile.__init__(self)
		self.setNeighborhoodSize(unit / 2)
		self.setColor(breve.vector(0, 0, 0))	
		
	def iterate(self):	
		breve.Mobile.iterate(self)
		self.neighborList = self.getNeighbors()
		while len(self.neighborList) > 0: 
			breve.deleteInstance(self.neighborList.pop())

class Fetcher(breve.Mobile):
	def __init__(self, position, sight):
		breve.Mobile.__init__(self)
		self.move(position)
		self.setNeighborhoodSize(sight)
	
	def iterate(self):
		breve.Mobile.iterate(self)
		self.neighborList = self.getNeighbors()
		
	def some(self, instance, count):
		instances = []
		while len(self.neighborList) > 0 and count > 0:
			bot = self.neighborList.pop()
			if isinstance(bot, instance):
				instances.append(bot)
				count -= 1
		return instances		
		
Controller()